package cn.ray.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import cn.ray.springframework.beans.BeansException;
import cn.ray.springframework.beans.PropertyValue;
import cn.ray.springframework.beans.factory.config.BeanDefinition;
import cn.ray.springframework.beans.factory.config.BeanReference;
import cn.ray.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import cn.ray.springframework.beans.factory.support.BeanDefinitionRegistry;
import cn.ray.springframework.core.io.Resource;
import cn.ray.springframework.core.io.ResourceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry, resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            try (InputStream inputStream = resource.getInputStream()) {
                doLoadBeanDefinitions(inputStream);
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new BeansException("ioexception parsing xml document form" + resource, e);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        //解析resource列表
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        //将location指向的资源解析
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeansException {
        for(String str:locations){
            loadBeanDefinitions(str);
        }
    }

    //对xml文件进行解析
    private void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
        //从输入流中读取xml文本，检查xml结构后，将xml文档转换成dom树
        Document doc = XmlUtil.readXML(inputStream);

        //获取dom树的根元素，也就是xml文件的最外层结构
        Element root = doc.getDocumentElement();

        //获取所有的子节点 放入列表 以便后文循环解析
        NodeList childNodes = root.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            //判断元素 例如注释节点，纯文本节点都会被跳过
            if (!((childNodes.item(i)) instanceof Element)) continue;

            //此处会过滤那些名称不为Bean的元素 例如<head></head> <custom-tag>等
            if (!childNodes.item(i).getNodeName().equals("bean")) continue;

            //此时得到Bean元素 开始解析
            Element bean = (Element) childNodes.item(i);
            //getAttribute解析标签为bean的节点中的属性
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");

            //增加对initMethod和destroyMethod的读取
            String initMethod=bean.getAttribute("init-method");
            String destroyMethod=bean.getAttribute("destroy-method");
            String beanScope=bean.getAttribute("scope");

            //获取Bean类中Class 这个Class对象包含了这个类的唯一信息
            Class<?> clazz = Class.forName(className);
            //优先级id>name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            //如果id和name都没有，则通过Class名赋值
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            //定义bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);

            beanDefinition.setInitMethodName(initMethod);
            beanDefinition.setDestroyMethodName(destroyMethod);

            //small-10 迭代scope的解析，并将scope属性填充到BeanDefinition中
            if(StrUtil.isNotEmpty(beanScope))
                beanDefinition.setScope(beanScope);

            for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
                if (!(bean.getChildNodes().item(j) instanceof Element)) continue;
                if (!bean.getChildNodes().item(j).getNodeName().equals("property")) continue;
                //解析标签：property
                //模式：<property name="dataSource" ref="dataSource"/>
                //<property name="url" value="jdbc:mysql://localhost:3306/springlearning"/>
                Element property= (Element) bean.getChildNodes().item(j);
                String attrName= property.getAttribute("name");
                String attrValue=property.getAttribute("value");
                String attrRef=property.getAttribute("ref");

                //筛选属性值,并加入BeanDefinition
                Object value=StrUtil.isNotEmpty(attrRef)?new BeanReference(attrRef):attrValue;
                PropertyValue propertyValue=new PropertyValue(attrName,value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }

            //如果此时容器中已经有Bean定义 抛出异常
            if(getRegistry().containsBeanDefinition(beanName)){
                throw new BeansException("duplicate beanName{"+beanName+"}is not allowed");
            }

            //注册beanDefinition
            getRegistry().registerBeanDefinition(beanName,beanDefinition);
        }
    }
}
